<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<div th:replace="~{commons/commons::head}"></div>

<body>
<!-- 顶部导航栏 -->
<div th:replace="~{commons/commons::topbar}"></div>

<div class="container-fluid">
    <div class="row">
        <!-- 侧边栏 -->
        <div th:replace="~{commons/commons::siderbar(active='ssrf.html')}"></div>

        <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
            <div class="vul_header">
                <span>SSRF</span>
            </div>
            <hr>

            <ul class="nav nav-tabs">
                <li class="nav-item">
                    <a class="nav-link active" data-toggle="tab" href="#aa"><span class="lnr lnr-bug"></span>
                        漏洞描述</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" data-toggle="tab" href="#bb"><span class="lnr lnr-bullhorn"></span> 安全编码</a>
                </li>
            </ul>

            <div class="tab-content">
                <div class="tab-pane dec shadow-sm p-3 mb-4 rounded active" id="aa">
                    SSRF(Server-Side Request Forgery) 服务器端请求伪造，是一种由攻击者构造形成由服务端发起请求的一个安全漏洞。
                </div>
                <div class="tab-pane fade" id="bb">
                    <textarea disabled="disabled" class="form-control shadow-sm p-3 mb-5 rounded" id="coder"
                              style="height: 160px;">
【必须】避免直接访问不可信地址
1.服务器访问不可信地址时，禁止访问私有地址段及内网域名。
2.建议通过URL解析函数进行解析，获取host或者domain后通过DNS获取其IP，然后和内网地址进行比较。
3.对已校验通过地址进行访问时，应关闭跟进跳转功能。
4.或者只允许访问白名单内的域名。
                    </textarea>
                </div>
            </div>

            <hr>

            <div class="box-float">
                <div class="float1">

                    <a target="_blank" style="float:right" class="btn btn-sm btn-danger"
                       th:href="@{/SSRF/URLConnection/vul?url=file:///etc/passwd}">
                        <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor"
                             viewBox="0 0 16 16">
                            <path d="m12.14 8.753-5.482 4.796c-.646.566-1.658.106-1.658-.753V3.204a1 1 0 0 1 1.659-.753l5.48 4.796a1 1 0 0 1 0 1.506z"/>
                        </svg>
                        <span class="align-middle"> Run</span></a>
                    <h5><span class="lnr lnr-bug"> 漏洞代码</span></h5>
                    <label for="code1"></label><textarea class="form-control" id="code1">
// url参数没做限制，可调用URLConnection发起任意请求，比如请求内网，或使用file等协议读取文件

public static String URLConnection(String url) {
    try {
        URL u = new URL(url);
        URLConnection conn = u.openConnection();
        BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        String content;
        StringBuffer html = new StringBuffer();

        while ((content = reader.readLine()) != null) {
            html.append(content);
        }
        reader.close();
        return html.toString();

    } catch (Exception e) {
        return e.getMessage();
    }
}
                    </textarea><br><br>


                    <a target="_blank" style="float:right" class="btn btn-sm btn-danger"
                       th:href="@{/SSRF/URLConnection/vul2?url=http://168302434}">
                        <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor"
                             viewBox="0 0 16 16">
                            <path d="m12.14 8.753-5.482 4.796c-.646.566-1.658.106-1.658-.753V3.204a1 1 0 0 1 1.659-.753l5.48 4.796a1 1 0 0 1 0 1.506z"/>
                        </svg>
                        <span class="align-middle"> Run</span></a>
                    <h5><span class="lnr lnr-bug"> 漏洞代码 - 绕过</span></h5>
                    <label for="code2"></label><textarea class="form-control" id="code2">
// SSRF修复经常碰到的问题，虽然过滤了内网地址，但通过短链接跳转、IP进制的方式可以绕过

public String URLConnection2(String url) {
    if (!Security.isHttp(url)) {
        return "不允许非http协议!!!";
    } else if (Security.isIntranet(Security.urltoIp(url))) {
        return "不允许访问内网!!!";
    } else {
        return HttpClientUtils.URLConnection(url);
    }
}
                    </textarea>
                </div>


                <div class="float2">

                    <a target="_blank" style="float:right" class="btn btn-sm btn-success"
                       th:href="@{/SSRF//URLConnection/safe?url=http://192.168.1.1}">
                        <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor"
                             viewBox="0 0 16 16">
                            <path d="m12.14 8.753-5.482 4.796c-.646.566-1.658.106-1.658-.753V3.204a1 1 0 0 1 1.659-.753l5.48 4.796a1 1 0 0 1 0 1.506z"/>
                        </svg>
                        <span class="align-middle"> Run</span></a>
                    <h5><span class="lnr lnr-smile"> 安全代码 - 白名单方式</span></h5>
                    <label for="code4"></label><textarea class="form-control" id="code4">
// 白名单是最佳修复方案
public String URLConnection3(String url) {
    if (!Security.isHttp(url)) {
        return "不允许非http/https协议!!!";
    } else if (!Security.isWhite(url)) {
        return "非可信域名！";
    } else {
        return HttpClientUtil.URLConnection(url);
    }
}
                    </textarea><br><br>


                    <a target="_blank" style="float:right" class="btn btn-sm btn-success"
                       th:href="@{/SSRF/HTTPURLConnection/safe?url=http://192.168.1.1}">
                        <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" fill="currentColor"
                             viewBox="0 0 16 16">
                            <path d="m12.14 8.753-5.482 4.796c-.646.566-1.658.106-1.658-.753V3.204a1 1 0 0 1 1.659-.753l5.48 4.796a1 1 0 0 1 0 1.506z"/>
                        </svg>
                        <span class="align-middle"> Run</span></a>
                    <h5><span class="lnr lnr-smile"> 安全代码 - 过滤方式</span></h5>
                    <label for="code3"></label><textarea class="form-control" id="code3">
public String HTTPURLConnection(String url) {
    // 校验 url 是否以 http 或 https 开头
    if (!Security.isHttp(url)) {
        log.error("[HTTPURLConnection] 非法的 url 协议：" + url);
        return "不允许非http/https协议!!!";
    }

    // 解析 url 为 IP 地址
    String ip = Security.urltoIp(url);
    log.info("[HTTPURLConnection] SSRF解析IP：" + ip);

    // 校验 IP 是否为内网地址
    if (Security.isIntranet(ip)) {
        log.error("[HTTPURLConnection] 不允许访问内网：" + ip);
        return "不允许访问内网!!!";
    }

    try {
        return HttpClientUtils.HTTPURLConnection(url);
    } catch (Exception e) {
        log.error("[HTTPURLConnection] 访问失败：" + e.getMessage());
        return "访问失败，请稍后再试!!!";
    }
}

// 1. 判断是否为http协议
public static boolean isHttp(String url) {
    return url.startsWith("http://") || url.startsWith("https://");
}

// 2. 解析IP地址
public static String urltoIp(String url) {
    try {
        URI uri = new URI(url);
        String host = uri.getHost().toLowerCase();
        InetAddress ip = Inet4Address.getByName(host);
        return ip.getHostAddress();
    } catch (Exception e) {
        return "127.0.0.1";
    }
}

// 3. 判断是否为内网IP
public static boolean isIntranet(String url) {
    Pattern reg = Pattern.compile("^(127\\.0\\.0\\.1)|(localhost)|^(10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})|^(172\\.((1[6-9])|(2\\d)|(3[01]))\\.\\d{1,3}\\.\\d{1,3})|^(192\\.168\\.\\d{1,3}\\.\\d{1,3})$");
    Matcher match = reg.matcher(url);
    Boolean a = match.find();
    return a;
}

// 4. 不允许302跳转
HttpURLConnection conn = (HttpURLConnection) u.openConnection();
conn.setInstanceFollowRedirects(false); // 不允许重定向或者对重定向后的地址做二次判断
conn.connect();
                    </textarea>
                </div>

            </div>
        </main>
    </div>
</div>

<!-- 引入script -->
<div th:replace="~{commons/commons::script}"></div>


</body>

</html>